'use strict';

const HttpStatus = require('http-status-codes');
const validators = require('async-validator');
const _ = require('lodash');

module.exports = {
  ycHeaders() {
    const {
      userId = '',
      trackId = '',
      role = '',
      merchantId = '',
      merchantStaffId = '',
      merchantChainId = '',
      operatorStaffId = '',
      driverId = '',
      designatedStaffId = '',
      designatedPlatId = '',
      isolation = 0,
      distributorId = '',
      distributorStaffId = '',
    } = this;

    const ip = this.get('X-Forwarded-For') || this.ip;

    const headers = {
      'yc-request-from': 'node.js',
      'yc-user-id': userId,
      'yc-request-id': trackId,
      'yc-user-role': role,
      'yc-merchant-id': merchantId,
      'yc-merchant-staff-id': merchantStaffId,
      'yc-operate-staff-id': operatorStaffId,
      'yc-driver-id': driverId,
      'yc-designated-staff-id': designatedStaffId,
      'yc-designated-plat-id': designatedPlatId,
      'yc-isolation-id': isolation,
      'yc-distributor-staff-id': distributorStaffId,
      'yc-distributor-id': distributorId,
      'yc-version': this.get('yc-version'),
      'yc-ad-code': this.get('yc-ad-code'),
      'yc-plat-id': this.get('yc-plat-id'),
      'yc-lng': this.get('yc-lng'),
      'yc-lat': this.get('yc-lat'),
      'yc-merchant-chain-id': merchantChainId,
      'yc-ip': ip,
      'yc-device-id': this.get('device-id'),
    };
    return _.pickBy(headers, item => {
      return !_.isNull(item) && item !== '';
    });
  },
  error({
    code = -1,
    status = 200,
    message,
    data = undefined,
    debug = undefined,
  }) {
    this.status = status;
    this.body = {
      status,
      code,
      message,
      data,
      debug,
      requestId: this.trackId,
    };
  },
  success({ data, code = 0, message = 'ok' }) {
    const body = {
      code,
      message,
      data,
      requestId: this.trackId,
    };

    this.status = HttpStatus.OK;
    this.body = body;
  },
  validate(rules, data = null) {
    if (!data) {
      data = this.method === 'GET' ? this.query : this.request.body;
    }
    return new Promise(resolve => {
      new validators(rules).validate(data, errors => {
        if (errors) {
          const { message } = errors[0];
          return this.throw(HttpStatus.OK, message);
        }
        resolve();
      });
    });
  },
  async QueryPage({ pageNum = 1, pageSize = 20 }, callback) {
    const { is } = this.helper;
    pageNum = +pageNum;
    pageSize = +pageSize;
    if (!is().Number(pageNum)) pageNum = 1;
    if (!is().Number(pageSize)) pageSize = 20;
    if (pageNum < 1) pageNum = 1;
    if (pageSize <= 0) pageSize = 20;
    if (pageSize > 1000) pageSize = 1000;
    const presult = await Promise.all([
      callback().count(),
      callback()
        .skip((pageNum - 1) * pageSize)
        .limit(pageSize),
    ]);
    const total = presult[0];
    const list = presult[1];
    return {
      pageSize,
      pageNum,
      total,
      list,
    };
  },
  async ycGet(path, query, headers) {
    return await this.ycRequest(path, { data: query, headers });
  },
  async ycPost(path, body, headers) {
    return await this.ycRequest(path, { data: body, method: 'POST', headers });
  },
  async ycRequest(path, option) {
    const baseUrl = this.app.config.GATEWAY_HOST || '127.0.0.1:8040';
    const fullPath = path.startsWith('http') ? path : baseUrl + path;
    const headers = _.merge(this.ycHeaders(), option.headers);
    option = Object.assign(
      {
        method: 'GET',
        dataType: 'json',
        contentType: 'json',
        timing: true,
        timeout: 60000,
        httpAgent: {
          // 默认开启 http KeepAlive 功能
          keepAlive: true,
          // 空闲的 KeepAlive socket 最长可以存活 4 秒
          freeSocketKeepAliveTimeout: 4000,
          timeout: 30000,
          // 允许创建的最大 socket 数
          maxSockets: Number.MAX_SAFE_INTEGER,
          // 最大空闲 socket 数
          maxFreeSockets: 256,
        },
      },
      option
    );
    option.headers = headers;
    const log = {
      url: fullPath,
      method: option.method,
      query: option.method === 'GET' ? option.data : undefined,
      data: option.method === 'POST' ? option.data : undefined,
      headers: option.headers,
      result: {},
      timing: {},
    };
    try {
      const { method, url, headers, query, data } = log;
      const params = JSON.stringify(data || query);
      const headersStr = JSON.stringify(
        _.pickBy(headers, value => value !== '')
      );
      this.logger.info(
        `Method: ${method} URL: ${url} Header: ${headersStr} params: ${params}`
      );
      const result = await this.curl(fullPath, option);
      log.result = result.data;
      log.timing = result.res.timing;
      this.httpLogger(log);
      return result.data.data;
    } catch (error) {
      log.result = (error.data && error.data.response) || error;
      this.httpLogger(log, 'error');
    }
  },
  httpLogger(res, type = '') {
    const isbody = type === 'error' || res.result.code !== 0;
    const { url, method, timing } = res;
    const result = isbody
      ? res.result
      : { code: res.result.code, message: res.result.message };
    this.logger.info(
      `Method: ${method} URL: ${url} Result: ${JSON.stringify(
        result
      )} Timing: ${JSON.stringify(timing)}`
    );
    if (res.result.code !== 0) {
      const debug = {
        path: res.url,
        query: res.query,
        data: res.data,
        headers: this.ycHeaders(),
        response: res.result,
        method,
      };
      const code = {
        code: res.result.code || -1,
        message: res.result.message || '服务端内部问题',
      };

      if (process.env.EGG_SERVER_ENV !== 'prod') {
        code.debug = debug;
      }
      this.throw(200, res.result.message || '服务端内部问题', code);
    }
  },
};
